ShowClass.java

package reflection;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.stream.Collectors;


/**
 * A program that displays a class synopsis for the named class
 *
 * This example is from _Java Examples in a Nutshell_. (http://www.oreilly.com)
 *
 * This example is provided WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, modify, and distribute it for non-commercial purposes.
 * For any commercial use, see http://www.davidflanagan.com/javaexamples
 *
 * @author David Flanagan
 * @author Robert C. Duvall
 */
public class ShowClass {
    /**
     * Tries to create an object using both a default constructor and one that takes a String.
     */
    public static void makeClass (Class<?> clazz) {
        try {
            // call no-arg constructor
            Object o = clazz.getDeclaredConstructor().newInstance();
            System.out.println("Printing: " + o);

            // call constructor whose parameter matches the given class type
            o = clazz.getDeclaredConstructor(String.class).newInstance("Test");
            System.out.println("Printing: " + o);
        }
        catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            // FIXME: NOT ideal, but just a basic test program
            e.printStackTrace();
        }
    }

    /**
     * Display modifiers, name, superclass and interfaces of a class or interface.
     * Then list all constructors, fields, and methods.
     */
    public static void printClass (Class<?> c) {
        // print any annotations before class
        printAnnotations(c.getAnnotations(), "\n");
        // print modifiers, type (class or interface), name and superclass
        System.out.print(Modifier.toString(c.getModifiers()) +
                         // modifiers will include the "interface" keyword here...
                         (c.isInterface() ? " " : " class ") +
                         c.getName());
        if (c.getSuperclass() != null) {
            System.out.print(" extends " + c.getSuperclass().getName());
        }
        // print interfaces or super-interfaces of the class or interface.
        Class<?>[] interfaces = c.getInterfaces();
        if (interfaces.length > 0) {
            System.out.println(c.isInterface() ? " extends " : " implements ");
            printTypes(interfaces);
        }
        // begin class member listing
        System.out.println(" {");
        System.out.println("  // Constructors");
        for (Constructor<?> constructor : c.getDeclaredConstructors()) {
            printMethodOrConstructor(constructor);
        }
        System.out.println("  // Fields");
        for (Field field : c.getDeclaredFields()) {
            printField(field);
        }
        System.out.println("  // Methods");
        for (Method method : c.getDeclaredMethods()) {
            printMethodOrConstructor(method);
        }
        System.out.println("}");
    }

    // Print the modifiers, type, and name of a field
    private static void printField (Field f) {
        // print indentation
        System.out.print("  ");
        // print any annotations
        printAnnotations(f.getAnnotations(), " ");
        // print any modifiers, type, and name
        System.out.println(modifiers(f.getModifiers()) + typename(f.getType()) + " " + f.getName() + ";");
    }

    // Print method or constructor modifiers: return type, name, parameter types, and exceptions.
    private static void printMethodOrConstructor (Executable member) {
        // print indentation
        System.out.print("  ");
        // print any annotations
        printAnnotations(member.getAnnotations(), " ");
        // print any modifiers
        System.out.print(modifiers(member.getModifiers()));
        // print return type if present
        if (member instanceof Method m) {
            System.out.print(typename(m.getReturnType()) + " ");
        }
        // print name
        System.out.print(member.getName() + "(");
        // print any parameters
        printTypes(member.getParameterTypes());
        System.out.print(")");
        // print any exceptions thrown
        Class<?>[] exceptions = member.getExceptionTypes();
        if (exceptions.length > 0) {
            System.out.print(" throws ");
        }
        printTypes(exceptions);
        System.out.println(";");
    }

    // Print a list of types
    private static void printAnnotations (Annotation[] annotations, String delimiter) {
        for (Annotation a : annotations) {
            System.out.print(a + delimiter);
        }
    }

    // Print a list of types
    private static void printTypes (Class<?>[] types) {
        // preferred way as of Java 8
        System.out.print(Arrays.stream(types)
                               .map(ShowClass::typename)
                               .collect(Collectors.joining(", ")));
        // OLD:
        //for (int k = 0; k < types.length; k += 1) {
        //    if (k > 0) {
        //        System.out.print(", ");
        //    }
        //    System.out.print(typename(types[k]));
        //}
    }
    
    // Return name of an interface or primitive type, handling arrays.
    private static String typename (Class<?> t) {
        StringBuilder brackets = new StringBuilder();
        while (t.isArray()) {
            brackets.append("[]");
            t = t.getComponentType();
        }
        return t.getName() + brackets;
    }

    // Return a string version of modifiers, handling spaces nicely.
    private static String modifiers (int m) {
        return (m == 0) ? "" : Modifier.toString(m) + " ";
    }


    // Simple example to show how to use this class
    public static void main (String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName((args.length > 0) ? args[0] : "java.lang.String");
        printClass(clazz);
        makeClass(clazz);
    }
}